Skip to content

Fix #12646: project-local-repo clean race condition when root pom has parent - #12650

Draft
gnodet wants to merge 2 commits into
masterfrom
fix-12646-project-local-repo-clean-race-conditio
Draft

Fix #12646: project-local-repo clean race condition when root pom has parent#12650
gnodet wants to merge 2 commits into
masterfrom
fix-12646-project-local-repo-clean-race-conditio

Conversation

@gnodet

@gnodet gnodet commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Move project-local-repo from target/project-local-repo to .mvn/project-local-repo so it is not affected by maven-clean-plugin's deletion of target/.

This eliminates the race condition in parallel builds (mvn clean install -T N) where the root project's maven-clean-plugin deletes target/ while sibling modules concurrently write artifacts into target/project-local-repo.

Root cause

In Maven 4, clean and install phases are placed into the same TaskSegment, so they execute concurrently across projects in parallel builds. When the root pom.xml has a parent (e.g. a super-pom in the reactor), MultiThreadedBuilder schedules the parent first, then runs the root project and child modules in parallel. The root project's clean phase (via maven-clean-plugin) races with sibling modules installing artifacts into target/project-local-repo.

Fix

Move project-local-repo from target/ to .mvn/, making it structurally impossible for maven-clean-plugin to interfere. ReactorReader fully owns the lifecycle:

  • Cleanup: per-GAV cleanup when a project enters its clean phase (preserves sibling artifacts for partial/resumable builds)
  • Install: on ProjectSucceeded, artifacts are hard-linked (or copied) into .mvn/project-local-repo

This approach is simpler than the previous lock-based fix — the ReentrantReadWriteLock and isProjectLocalRepoOwner() are removed entirely.

Precedent

Storing build-related data outside target/ (surviving clean) is established:

  • Maven Build Cache Extension~/.m2/.cache/maven-build-cache
  • Gradle.gradle/ directory at project root (universally gitignored)

Trade-off

.mvn/project-local-repo should be added to .gitignore — similar to how Gradle projects gitignore .gradle/.

Fixes #12646

🤖 Generated with Claude Code

gnodet and others added 2 commits July 31, 2026 15:51
… parent

When the root pom.xml has a parent (super-pom) in the reactor,
MultiThreadedBuilder schedules the super-pom first, then builds the
root pom and sibling modules in parallel. The root pom's clean phase
(maven-clean-plugin) deletes the entire target/ directory, which
includes project-local-repo. Concurrently, sibling modules that
complete successfully write artifacts into target/project-local-repo
via ReactorReader.installIntoProjectLocalRepository(). This race
causes maven-clean-plugin to fail because the directory is being
written to while it tries to delete it.

Fix: Add a ReentrantReadWriteLock to ReactorReader that coordinates
access to the project-local-repo directory between clean and install
operations. When the project whose build directory contains the
project-local-repo enters its clean phase, a write lock is acquired
to block concurrent installs. The write lock is released when the
clean mojo completes (succeeds or fails). Install operations acquire
a read lock, allowing multiple concurrent installs but blocking while
the owning project's clean is running.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e clean race

Move the project-local-repo directory from target/project-local-repo to
.mvn/project-local-repo so it is not affected by maven-clean-plugin's
deletion of target/. This eliminates the race condition in parallel builds
where maven-clean-plugin deletes target/ while sibling modules concurrently
write artifacts into project-local-repo.

The previous lock-based approach prevented crashes but could not guarantee
ordering between clean and install operations. Moving the directory outside
target/ makes the race structurally impossible — ReactorReader fully owns
the lifecycle of project-local-repo (per-GAV cleanup on clean, install on
project success).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet gnodet added this to the 4.0.0-rc-7 milestone Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

project-local-repo clean race condition when root pom has parent

1 participant